package com.it.web.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.it.admin.event.ReadArticleEvent;
import com.it.admin.event.subscriber.ReadArticleSubscriber;
import com.it.common.domain.dos.*;
import com.it.common.enums.ResponseCodeEnum;
import com.it.common.exception.BizException;
import com.it.common.mapper.*;
import com.it.common.utils.PageResponse;
import com.it.common.utils.Response;
import com.it.web.markdown.MarkdownHelper;
import com.it.web.model.dto.article.FindArticleDetailDto;
import com.it.web.model.dto.article.FindIndexArticlePageListDto;
import com.it.web.model.vo.article.FindArticleDetailRspVO;
import com.it.web.model.vo.article.FindIndexArticlePageListRspVO;
import com.it.web.model.vo.article.FindPreNextArticleRspVO;
import com.it.web.model.vo.category.FindCategoryListRspVO;
import com.it.web.model.vo.tag.FindTagListRspVO;
import com.it.web.service.ArticleService;
import com.it.web.utils.MarkdownStatsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, ArticleDO> implements ArticleService {


    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private TagMapper tagMapper;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private ArticleTagRelMapper articleTagRelMapper;

    @Resource
    private ArticleCategoryRelMapper articleCategoryRelMapper;

    @Resource
    private ArticleContentMapper articleContentMapper;

    @Resource
    private ApplicationEventPublisher publisher;

    /**
     * 获取首页文章分页数据
     *
     * @param findIndexArticlePageListDto
     * @return {@link Response }
     */
    @Override
    public Response findArticlePageList(FindIndexArticlePageListDto findIndexArticlePageListDto) {

        Long current = findIndexArticlePageListDto.getCurrent();
        Long size = findIndexArticlePageListDto.getSize();

        // 第一步：分页查询文章主体记录
        Page<ArticleDO> articleDOPage = articleMapper.selectPageList(current, size, null, null, null,null);

        List<ArticleDO> articleDOList = articleDOPage.getRecords();

        List<FindIndexArticlePageListRspVO> vos = null;
        if(Objects.nonNull(articleDOList)){
            // 文章 DO 转 VO
            vos = articleDOList.stream().map(articleDO -> FindIndexArticlePageListRspVO.builder().id(articleDO.getId())
                    .isTop(articleDO.getWeight() > 0)
                    .cover(articleDO.getCover()).title(articleDO.getTitle()).createDate(LocalDate.from(articleDO.getCreateTime()))
                    .summary(articleDO.getSummary()).build()).collect(Collectors.toList());

            //拿到文章ID集合
            List<Long> articleIds = articleDOList.stream().map(articleDO -> articleDO.getId()).collect(Collectors.toList());

            // 第二步：设置文章所属分类
            // 查询所有分类
            List<CategoryDO> categoryDOS = categoryMapper.findCategorySelectList();
            // 转 Map, 方便后续根据分类 ID 拿到对应的分类名称
            Map<Long,String> categoryIdNameMap = categoryDOS.stream()
                    .collect(Collectors.toMap(categoryDO -> categoryDO.getId(), categoryDO -> categoryDO.getName()));

            // 根据文章 ID 批量查询所有关联记录
            List<ArticleCategoryRelDO> articleCategoryRelDOS = articleCategoryRelMapper.selectByArticleIds(articleIds);
            System.out.println("-0--00==="+articleCategoryRelDOS);
            vos.forEach(vo -> {
                Long currArticleId = vo.getId();
                // 过滤出当前文章对应的关联数据
                Optional<ArticleCategoryRelDO> operation = articleCategoryRelDOS.stream()
                        .filter(articleCategoryRelDO -> articleCategoryRelDO.getArticleId() == currArticleId).findAny();
                // 若不为空
                if(operation.isPresent()){
                    ArticleCategoryRelDO articleCategoryRelDO = operation.get();
                    Long currCategoryId = articleCategoryRelDO.getCategoryId();
                    // 通过分类 ID 从 map 中拿到对应的分类名称
                    String categoryName = categoryIdNameMap.get(currCategoryId);
                    FindCategoryListRspVO findCategoryListRspVO = FindCategoryListRspVO.builder()
                            .id(currCategoryId)
                            .name(categoryName)
                            .build();
                    // 设置到当前 vo 类中
                    vo.setCategory(findCategoryListRspVO);
                }
            });
            // 第三步：设置文章标签
            // 查询所有标签
            List<TagDO> tagDOS = tagMapper.findAll();
            // 转 Map, 方便后续根据标签 ID 拿到对应的标签名称
            Map<Long,String> tagNameMap = tagDOS.stream().collect(Collectors.toMap(TagDO::getId, TagDO::getName));
            // 拿到所有文章的标签关联记录
            List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleIds(articleIds);

            vos.forEach(vo -> {
                Long currArticleId = vo.getId();
                // 过滤出当前文章的标签关联记录
                List<ArticleTagRelDO> articleTagRelDOList = articleTagRelDOS.stream()
                        .filter(articleTagRelDO -> articleTagRelDO.getArticleId() == currArticleId).collect(Collectors.toList());
                List<FindTagListRspVO> findTagListRspVOS = null;
                if(Objects.nonNull(articleTagRelDOList)){
                    // 将关联记录 DO 转 VO, 并设置对应的标签名称
                    findTagListRspVOS = articleTagRelDOList.stream().map(articleTagRelDO -> FindTagListRspVO.builder().id(articleTagRelDO.getTagId())
                            .name(tagNameMap.get(articleTagRelDO.getTagId())).build()).collect(Collectors.toList());

                    vo.setTags(findTagListRspVOS);
                }
            });
        }
        return PageResponse.success(articleDOPage, vos);
    }

    @Override
    public Response findArticleDetail(FindArticleDetailDto findArticleDetailDto) {
        Long articleId = findArticleDetailDto.getArticleId();

        ArticleDO articleDO = articleMapper.selectById(articleId);
        // 判断文章是否存在
        if (Objects.isNull(articleDO)) {
            log.warn("==> 该文章不存在, articleId: {}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }

        ArticleContentDO articleContentDO = articleContentMapper.selectByArticleId(articleId);
        String content = articleContentDO.getContent();
        // 计算 md 正文字数
        Integer totalWords = MarkdownStatsUtil.calculateWordCount(content);
        // DO 转 VO
        FindArticleDetailRspVO vo = FindArticleDetailRspVO.builder().title(articleDO.getTitle())
                .createTime(articleDO.getCreateTime()).readNum(articleDO.getReadNum())
                .totalWords(totalWords).readTime(MarkdownStatsUtil.calculateReadingTime(totalWords))
                .updateTime(articleDO.getUpdateTime())
                .content(MarkdownHelper.convertMarkdown2Html(articleContentDO.getContent())).build();

        // 查询所属分类
        ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId);
        CategoryDO categoryDO = categoryMapper.selectById(articleCategoryRelDO.getCategoryId());
        vo.setCategoryId(categoryDO.getId());
        vo.setCategoryName(categoryDO.getName());

        // 查询标签
        List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId);
        List<Long> tagIds = articleTagRelDOS.stream().map(articleTagRelDO -> articleTagRelDO.getTagId()).collect(Collectors.toList());
        List<TagDO> tagDOS = tagMapper.selectByIds(tagIds);
        // 标签 DO 转 VO
        List<FindTagListRspVO> tagVOS = tagDOS.stream().map(tagDO -> FindTagListRspVO.builder().id(tagDO.getId())
                .name(tagDO.getName()).build()).collect(Collectors.toList());

        vo.setTags(tagVOS);
        // 上一篇
        ArticleDO preArticleDO = articleMapper.selectPreArticle(articleId);
        if(Objects.nonNull(preArticleDO)){
            FindPreNextArticleRspVO preArticleVO = FindPreNextArticleRspVO.builder()
                    .articleId(preArticleDO.getId())
                    .articleTitle(preArticleDO.getTitle())
                    .build();
            vo.setPreArticle(preArticleVO);
        }
        //下一篇
        ArticleDO nextArticleDO = articleMapper.selectNextArticle(articleId);
        if(Objects.nonNull(nextArticleDO)){
            FindPreNextArticleRspVO nextArticleRspVO = FindPreNextArticleRspVO.builder()
                    .articleId(nextArticleDO.getId())
                    .articleTitle(nextArticleDO.getTitle())
                    .build();
            vo.setNextArticle(nextArticleRspVO);
        }

        publisher.publishEvent(new ReadArticleEvent(this,articleId));

        return Response.success(vo);
    }
}
